package zabbix

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"strconv"
)

/*
需求: 获取zabbix最近的告警事件
1. 通过登录方法获取接口凭证
2. 通过触发器方法获取已经触发的不同级别的问题事件
*/

// https://www.zabbix.com/documentation/3.4/zh/manual/api

// 第二个例子其实已经说明,只需要补充额外的条件即可满足需求
// https://www.zabbix.com/documentation/3.4/manual/api/reference/trigger/get

type request struct {
	JSONRPC string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params"`
	Auth    string      `json:"auth,omitempty"`
	ID      int         `json:"id"`
}

type response struct {
	JSONRPC string      `json:"jsonrpc"`
	Error   errorZbx    `json:"error"`
	Result  interface{} `json:"result"`
	ID      int         `json:"id"`
}

type errorZbx struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    string `json:"data"`
}

type Zbx struct {
	URL         string
	User        string
	Passwd      string
	MinSeverity int
	ID          int
	Auth        string
}

//Problem return result by trigger
type Problem struct {
	Priority    int
	Description string
}

func New(url string, user string, passwd string, minSeverity int) *Zbx {
	return &Zbx{
		URL:         fmt.Sprintf("%v/api_jsonrpc.php", url),
		User:        user,
		Passwd:      passwd,
		MinSeverity: minSeverity,
	}
}

// Login get authentication token
func (z *Zbx) Login() error {
	params := make(map[string]interface{})
	params["user"] = z.User
	params["password"] = z.Passwd

	res, err := z.sendRequest("user.login", params)
	if err != nil {
		return err
	}
	z.Auth = res.Result.(string)
	log.Println("info zbx auth token is", z.Auth)
	z.ID = res.ID
	return nil
}
func (z *Zbx) getProblemByTrigger(hostGroup ...string) (problems []*Problem) {
	params := make(map[string]interface{})
	// 指定返回的字段,  返回必带triggerid字段
	params["output"] = []string{"description", "priority"}
	// 根据级别逆向排序
	params["sortfield"] = "priority"
	params["sortorder"] = "DESC"
	// 已监控主机
	params["monitored"] = 1
	// 已启动的触发器
	params["active"] = 1
	// 已处于问题状态的触发器
	params["only_true"] = 1
	// 大于等于某级别的问题状态的触发器
	params["min_severity"] = z.MinSeverity
	// 跳过依赖于其他触发器的问题状态中的触发器
	params["skipDependent"] = 1
	// 展开触发器名称中的宏
	params["expandDescription"] = 1

	if len(hostGroup) == 1 {
		params["group"] = hostGroup[0]
	}

	data, err := z.sendRequest("trigger.get", params)
	if err != nil {
		log.Println("error", err)
		return
	}

	v := reflect.ValueOf(data.Result)
	if v.Kind() == reflect.Slice {
		for i := 0; i < v.Len(); i++ {
			items := v.Index(i).Elem()
			pro := &Problem{}
			for _, k := range items.MapKeys() {
				if k.String() == "description" {
					pro.Description = fmt.Sprintf("%v", items.MapIndex(k))
				}
				if k.String() == "priority" {
					pri, err := strconv.Atoi(fmt.Sprintf("%v", items.MapIndex(k)))
					if err != nil {
						log.Println("error", err)
					}
					pro.Priority = pri
				}
			}
			problems = append(problems, pro)
		}
	}
	return
}

// GetLastProblem Retrieving triggers in problem state
func (z *Zbx) GetLastProblem() (problems []*Problem) {
	return z.getProblemByTrigger()
}

// GetProblemByGroup Retrieving triggers in group problem state
func (z *Zbx) GetProblemByGroup(group string) (problems []*Problem) {
	return z.getProblemByTrigger(group)
}

func (z *Zbx) sendRequest(method string, params interface{}) (*response, error) {

	respZbx := &response{}

	req := request{
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
		ID:      z.ID + 1,
		Auth:    z.Auth,
	}

	data, err := json.Marshal(req)
	if err != nil {
		log.Println("error", err)
		return respZbx, err
	}

	// Post admin: unsupported protocol scheme "" （写错z.URL为z.User）
	resp, err := http.Post(z.URL, "application/json", bytes.NewBuffer(data))
	if err != nil {
		log.Println("error", err)
		return respZbx, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("error", err)
		return respZbx, err
	}

	err = json.Unmarshal(body, respZbx)
	if err != nil {
		log.Println("error", err)
		return respZbx, err
	}

	if respZbx.Error.Code != 0 {
		log.Println("error", fmt.Sprintf("%+v", respZbx.Error))
		return respZbx, err
	}

	return respZbx, nil
}
